﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.IO;
using System.Globalization;
using System.Web;
using System.Collections;
using System.Security;
using System.Web.Configuration;
using jQube.Web.UI.Controls;

namespace jQube.Web.UI
{
    internal class PageRenderingManager
    {
        private const string JSonContentType = "application/json; charset=utf-8";
        private const string JSonDelimiter = ",";

        private const string JSonResponseTypeToken = "responseType";
        private const string JSonFormToken = "form";
        private const string JSonHiddenFieldsToken = "hiddenFields";
        private const string JSonScriptsToken = "scripts";
        private const string JSonErrorToken = "error";
        private const string JSonUpdatePanelsToken = "updatePanels";

        public PageRenderingManager(PageRequestManager pageRequestManager)
        {
            this.PageRequestManager = pageRequestManager;
        }

        public PageRequestManager PageRequestManager { get; set; }

        public void OnPagePreRender(object sender, EventArgs e)
        {
            if (this.PageRequestManager.ScriptManager.IsAsyncPostBack)
            {
                this.PageRequestManager.ScriptManager.IPage.SetRenderMethodDelegate(this.RenderPageCallback);
            }
        }

        public void OnPageError(object sender, EventArgs e)
        {
            if (this.PageRequestManager.ScriptManager.IsAsyncPostBack)
            {
                ScriptManager scriptManager = this.PageRequestManager.ScriptManager;

                Exception ex = scriptManager.IPage.Server.GetLastError();

                AsyncPostBackErrorEventArgs arg = new AsyncPostBackErrorEventArgs(ex);
                scriptManager.OnAsyncPostBackError(new AsyncPostBackErrorEventArgs(ex));

                int httpCode = this.GetHttpCodeForException(ex);

                bool showAsyncErrorMessage = false;

                if (scriptManager.AllowCustomErrorsRedirect)
                {
                    bool hasRedirect = this.CustomErrorsSectionHasRedirect(httpCode);

                    if (!hasRedirect) showAsyncErrorMessage = true;
                }
                else
                {
                    showAsyncErrorMessage = true;
                }

                if (showAsyncErrorMessage)
                {
                    IHttpResponse response = scriptManager.IPage.Response;

                    response.Clear();
                    response.AddHeader("Content-Type", JSonContentType);

                    response.Write("{");

                    this.WriteValueToken(response.Output, JSonResponseTypeToken, (int)JSonResponseType.Error);
                    response.Write(JSonDelimiter);
                    this.WriteObjectToken(response.Output, JSonErrorToken, this.ParseException(ex));

                    response.Write("}");
                    response.Flush();
                    response.End();
                }
            }
        }

        private string ParseException(Exception ex)
        {
            StringBuilder jsonParsedException = new StringBuilder();

            jsonParsedException.AppendFormat("\"message\":\"{0}\"", this.ParseJSonStringValue(ex.Message));

            if (ex.StackTrace != null)
            {
                jsonParsedException.AppendFormat(",\"stackTrace\":\"{0}\"", this.ParseJSonStringValue(ex.StackTrace));
            }

            if (ex.InnerException != null)
            {
                jsonParsedException.Append(",\"innerException\":{");
                jsonParsedException.Append(this.ParseException(ex.InnerException));
                jsonParsedException.Append("}");
            }

            return jsonParsedException.ToString();
        }

        private string ParseJSonStringValue(string value)
        {
            return value
                .Replace("\r\n", "\\r\\n")
                .Replace("\"", "\\\"")
                .Replace("\\", "\\\\");
        }

        private int GetHttpCodeForException(Exception e)
        {
            HttpException he = e as HttpException;

            if (he != null)
            {
                return he.GetHttpCode();
            }
            else if (e is UnauthorizedAccessException)
            {
                return 401;
            }
            else if (e is PathTooLongException)
            {
                return 414;
            }

            if (e.InnerException != null)
                return GetHttpCodeForException(e.InnerException);

            return 500;
        }

        [SecurityCritical()]
        [SecuritySafeCritical()]
        private bool CustomErrorsSectionHasRedirect(int httpCode)
        {
            ScriptManager scriptManager = this.PageRequestManager.ScriptManager;

            bool hasRedirect = (scriptManager.CustomErrorsSection.DefaultRedirect != null);

            if (!hasRedirect)
            {
                if (scriptManager.CustomErrorsSection.Errors != null)
                {
                    foreach (CustomError error in scriptManager.CustomErrorsSection.Errors)
                    {
                        if (error.StatusCode == httpCode)
                        {
                            hasRedirect = true;
                            break;
                        }
                    }
                }
            }

            return hasRedirect;
        }

        private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
        {
            ScriptManager scriptManager = this.PageRequestManager.ScriptManager;
            IHttpResponse response = scriptManager.IPage.Response;

            response.Clear();
            response.AddHeader("Content-Type", JSonContentType);
            response.Cache.SetNoServerCaching();

            writer.Write("{");

            IHtmlForm form = scriptManager.IPage.Form;

            ParserStringWriter parserWriter = new ParserStringWriter();
            ParserHtmlTextWriter formWriter = new ParserHtmlTextWriter(parserWriter);

            form.RenderControl(formWriter);

            this.WriteValueToken(writer, JSonResponseTypeToken, (int)JSonResponseType.Success);
            writer.Write(JSonDelimiter);
            this.WriteObjectToken(writer, JSonFormToken, formWriter.JSonParsedForm);
            writer.Write(JSonDelimiter);
            this.WriteArrayToken(writer, JSonHiddenFieldsToken, parserWriter.JSonParsedHiddenFields);
            writer.Write(JSonDelimiter);
            this.WriteArrayToken(writer, JSonScriptsToken, parserWriter.JSonParsedScripts);
            writer.Write(JSonDelimiter);
            this.WriteArrayToken(writer, JSonUpdatePanelsToken, parserWriter.JSonParsedUpdatePanels);

            writer.Write("}");
            response.Flush();
            response.End();
        }

        private void WriteObjectToken(TextWriter writer, string token, string content)
        {
            writer.Write(string.Format("\"{0}\":{1}{2}{3}", token, "{", content, "}"));
        }

        private void WriteValueToken(TextWriter writer, string token, string content)
        {
            writer.Write(string.Format("\"{0}\":\"{1}\"", token, content));
        }

        private void WriteValueToken(TextWriter writer, string token, int content)
        {
            writer.Write(string.Format("\"{0}\":{1}", token, content));
        }

        private void WriteValueToken(TextWriter writer, string token, bool content)
        {
            writer.Write(string.Format("\"{0}\":{1}", token, content.ToString().ToLower()));
        }

        private void WriteArrayToken(TextWriter writer, string token, string content)
        {
            writer.Write(string.Format("\"{0}\":[{1}]", token, content));
        }
    }
}
